/*
 * Copyright(c) 2023 NeatLogic Co., Ltd. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package neatlogic.module.change.api;

import neatlogic.framework.asynchronization.threadlocal.TenantContext;
import neatlogic.framework.auth.core.AuthAction;
import neatlogic.framework.change.dto.*;
import neatlogic.framework.common.constvalue.ApiParamType;
import neatlogic.framework.common.constvalue.GroupSearch;
import neatlogic.framework.file.dao.mapper.FileMapper;
import neatlogic.framework.process.auth.PROCESS_BASE;
import neatlogic.framework.process.dao.mapper.ProcessTaskMapper;
import neatlogic.framework.process.dto.ProcessTaskStepVo;
import neatlogic.framework.process.stephandler.core.IProcessStepHandlerUtil;
import neatlogic.framework.restful.annotation.Description;
import neatlogic.framework.restful.annotation.Input;
import neatlogic.framework.restful.annotation.OperationType;
import neatlogic.framework.restful.annotation.Param;
import neatlogic.framework.restful.constvalue.OperationTypeEnum;
import neatlogic.framework.restful.core.privateapi.PrivateApiComponentBase;
import neatlogic.framework.scheduler.core.IJob;
import neatlogic.framework.scheduler.core.SchedulerManager;
import neatlogic.framework.scheduler.dto.JobObject;
import neatlogic.framework.scheduler.exception.ScheduleHandlerNotFoundException;
import neatlogic.framework.util.TimeUtil;
import neatlogic.framework.change.constvalue.ChangeAuditDetailType;
import neatlogic.framework.change.constvalue.ChangeAuditType;
import neatlogic.framework.change.constvalue.ChangeOperationType;
import neatlogic.module.change.dao.mapper.ChangeMapper;
import neatlogic.framework.change.exception.ChangeNoPermissionException;
import neatlogic.framework.change.exception.ChangeNotFoundException;
import neatlogic.module.change.schedule.plugin.ChangeAutoStartJob;
import neatlogic.module.change.service.ChangeService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service
@Transactional
@AuthAction(action = PROCESS_BASE.class)
@OperationType(type = OperationTypeEnum.OPERATE)
public class ChangeUpdateApi extends PrivateApiComponentBase {
    
    private static Logger logger = LoggerFactory.getLogger(ChangeUpdateApi.class);
    
	@Autowired
	private ChangeMapper changeMapper;
	
	@Autowired
	private FileMapper fileMapper;
	
	@Autowired
	private ProcessTaskMapper processTaskMapper;
	
	@Autowired
	private ChangeService changeService;
	
	@Autowired
    private SchedulerManager schedulerManager;

	@Autowired
    private IProcessStepHandlerUtil IProcessStepHandlerUtil;

	@Override
	public String getToken() {
		return "change/update";
	}

	@Override
	public String getName() {
		return "更新变更";
	}

	@Override
	public String getConfig() {
		return null;
	}
	
	@Input({
		@Param(name = "processTaskStepId", type = ApiParamType.LONG, desc = "工单步骤id"),
		@Param(name = "changeId", type = ApiParamType.LONG, isRequired = true, desc = "变更id"),
		@Param(name = "planStartEndTime", type = ApiParamType.JSONARRAY, desc = "计划起止时间"),
		@Param(name = "autoStart", type = ApiParamType.INTEGER, desc = "是否自动开始"),
		@Param(name = "startTimeWindow", type = ApiParamType.STRING, desc = "开始时间窗口"),
		@Param(name = "endTimeWindow", type = ApiParamType.STRING, desc = "结束时间窗口"),
		@Param(name = "content", type = ApiParamType.STRING, desc = "变更描述"),
		@Param(name = "fileIdList", type = ApiParamType.JSONARRAY, desc = "附件"),
		@Param(name = "source", type = ApiParamType.STRING, defaultValue = "pc", desc = "来源")
	})
	@Description(desc = "更新变更")
	@Override
	public Object myDoService(JSONObject jsonObj) throws Exception {
		Long changeId = jsonObj.getLong("changeId");
		ChangeVo change = changeMapper.getChangeById(changeId);
		if(change == null) {
			throw new ChangeNotFoundException(changeId);
		}
		/** 获取当前变更锁 **/
		changeMapper.getChangeLockById(changeId);
		String owner = changeMapper.getChangeUserByChangeId(changeId);
		if(StringUtils.isNotBlank(owner)) {
			change.setOwner(GroupSearch.USER.getValuePlugin() + owner);
		}
		if(!changeService.isEditableChange(change)) {
			throw new ChangeNoPermissionException(ChangeOperationType.EDITCHANGE.getText());
		}

		ChangeVo newChangeVo = new ChangeVo();
		boolean isUpdateBaseInfo = false;
        Integer autoStart = jsonObj.getInteger("autoStart");
        if(autoStart != null && !Objects.equals(change.getAutoStart(), autoStart)) {
            isUpdateBaseInfo = true;
            newChangeVo.setAutoStart(autoStart);
        }
        List<String> planStartEndTime = JSON.parseArray(JSON.toJSONString(jsonObj.getJSONArray("planStartEndTime")), String.class);
		if(planStartEndTime != null && !Objects.equals(change.getPlanStartEndTime(), planStartEndTime)) {
			isUpdateBaseInfo = true;
			newChangeVo.setPlanStartEndTime(planStartEndTime);
		}
		
        String startTimeWindow = jsonObj.getString("startTimeWindow");
		if(startTimeWindow != null && !Objects.equals(change.getStartTimeWindow(), startTimeWindow)) {
			isUpdateBaseInfo = true;
			newChangeVo.setStartTimeWindow(startTimeWindow);
			newChangeVo.setEndTimeWindow(change.getEndTimeWindow());
		}
        String endTimeWindow = jsonObj.getString("endTimeWindow");
		if(endTimeWindow != null && !Objects.equals(change.getEndTimeWindow(), endTimeWindow)) {
			isUpdateBaseInfo = true;
			newChangeVo.setEndTimeWindow(endTimeWindow);
			if(newChangeVo.getStartTimeWindow() == null) {
			    newChangeVo.setStartTimeWindow(change.getStartTimeWindow());
			}
		}

        boolean isUpdate = false;
		if(isUpdateBaseInfo) {
            isUpdate = true;
            newChangeVo.setId(changeId);
            changeMapper.updateChangeById(newChangeVo);
		}

        String content = jsonObj.getString("content");
        if(content != null) {
            newChangeVo.setContent(content);
            String contentHash = changeMapper.getChangeDescriptionContentHashByChangeId(changeId);
            if(StringUtils.isNotBlank(content)) {
                ChangeContentVo contentVo = new ChangeContentVo(newChangeVo.getContent());
                if(!Objects.equals(contentVo.getHash(), contentHash)) {
                    if(contentHash != null) {
                        change.setContent(changeMapper.getChangeContentByHash(contentHash));
                        changeMapper.deleteChangeDescriptionByChangeId(changeId);
                    }
                    changeMapper.replaceChangeContent(contentVo);
                    changeMapper.insertChangeDescription(new ChangeDescriptionVo(changeId, contentVo.getHash()));
                    isUpdate = true;
                }
            }else if(contentHash != null) {
                change.setContent(changeMapper.getChangeContentByHash(contentHash));
                changeMapper.deleteChangeDescriptionByChangeId(changeId);
                isUpdate = true;
            }
        }
		

        List<Long> newFileIdList = JSON.parseArray(JSON.toJSONString(jsonObj.getJSONArray("fileIdList")), Long.class);
        if(newFileIdList != null) {
            List<Long> fileIdList = changeMapper.getChangeFileIdListByChangeId(changeId);
            if(!Objects.equals(newFileIdList, fileIdList)) {
                if(CollectionUtils.isNotEmpty(fileIdList)) {
                    change.setFileList(fileMapper.getFileListByIdList(fileIdList));
                    changeMapper.deleteChangeFileByChangeId(changeId);
                }
                if(CollectionUtils.isNotEmpty(newFileIdList)) {
                    newChangeVo.setFileList(fileMapper.getFileListByIdList(newFileIdList));
                    for(Long fileId : newFileIdList) {
                        changeMapper.insertChangeFile(new ChangeFileVo(changeId, fileId));
                    }
                }
                isUpdate = true;
            }
        }
		String source = jsonObj.getString("source");
		if(isUpdate) {
		    ProcessTaskStepChangeVo processTaskStepChangeVo = changeMapper.getProcessTaskStepChangeHandleByChangeId(changeId);
		    if(processTaskStepChangeVo != null) {
		        ProcessTaskStepVo processTaskStepVo = processTaskMapper.getProcessTaskStepBaseInfoById(processTaskStepChangeVo.getProcessTaskStepId());
		        if(processTaskStepVo != null && Objects.equals(processTaskStepVo.getIsActive(), 1)) {
		            IJob jobHandler = SchedulerManager.getHandler(ChangeAutoStartJob.class.getName());
	                if(jobHandler == null) {
	                    throw new ScheduleHandlerNotFoundException(ChangeAutoStartJob.class.getName());
	                }
	                JobObject.Builder jobObjectBuilder = new JobObject
	                    .Builder(changeId.toString(), jobHandler.getGroupName(), jobHandler.getClassName(), TenantContext.get().getTenantUuid());
	                JobObject jobObject = jobObjectBuilder.build();
	                if(Objects.equals(change.getAutoStart(), 1)) {
	                    if(Objects.equals(autoStart, 0)) {
	                        changeMapper.deleteChangeAutoStartByChangeId(changeId);
	                        schedulerManager.unloadJob(jobObject);
	                    }else {
	                        if(newChangeVo.getPlanStartTime() != null) {
	                            if(!Objects.equals(change.getPlanStartTime(), newChangeVo.getPlanStartTime())) {
	                                changeMapper.deleteChangeAutoStartByChangeId(changeId);
	                                schedulerManager.unloadJob(jobObject);
	                                try {
	                                    Date planStartTime = new SimpleDateFormat(TimeUtil.YYYY_MM_DD_HH_MM).parse(newChangeVo.getPlanStartTime());
	                                    if(planStartTime.after(new Date())) {
											/* 如果还没到计划开始时间，则启动定时器 **/
	                                        ChangeAutoStartVo changeAutoStartVo = new ChangeAutoStartVo(changeId, planStartTime);
	                                        changeMapper.insertChangeAutoStart(changeAutoStartVo);
	                                        jobHandler.reloadJob(jobObject);
	                                    }else {
											/* 如果过了计划开始时间，则开始变更 **/
	                                        changeService.startChangeById(changeId, source);
	                                    }
	                                } catch (ParseException e) {
	                                    logger.error(e.getMessage(), e);
	                                }
	                            } 
	                        }
	                    }
	                }else {
	                    if(Objects.equals(autoStart, 1)) {
	                        String planStartTimeStr = change.getPlanStartTime();
	                        if(StringUtils.isNotBlank(newChangeVo.getPlanStartTime())) {
	                            planStartTimeStr = newChangeVo.getPlanStartTime();
	                        }
	                        try {
	                            Date planStartTime = new SimpleDateFormat(TimeUtil.YYYY_MM_DD_HH_MM).parse(planStartTimeStr);
	                            if(planStartTime.after(new Date())) {
	                                /** 如果还没到计划开始时间，则启动定时器 **/
	                                ChangeAutoStartVo changeAutoStartVo = new ChangeAutoStartVo(changeId, planStartTime);
	                                changeMapper.insertChangeAutoStart(changeAutoStartVo);
	                                jobHandler.reloadJob(jobObject);
	                            }else {
	                                /** 如果过了计划开始时间，则开始变更 **/
	                                changeService.startChangeById(changeId, source);
	                            }
	                        } catch (ParseException e) {
	                            logger.error(e.getMessage(), e);
	                        }
	                    }
	                }
		        }
		    }
            
			/** 生成活动 **/
			ProcessTaskStepVo currentProcessTaskStepVo = new ProcessTaskStepVo();
			Long processTaskId = changeMapper.getProcessTaskIdByChangeId(changeId);
			currentProcessTaskStepVo.setProcessTaskId(processTaskId);
			Long currentProcessTaskStepId = jsonObj.getLong("processTaskStepId");
			currentProcessTaskStepVo.setId(currentProcessTaskStepId);
			currentProcessTaskStepVo.getParamObj().put(ChangeAuditDetailType.CHANGEINFO.getParamName(), JSON.toJSONString(newChangeVo));
			currentProcessTaskStepVo.getParamObj().put(ChangeAuditDetailType.CHANGEINFO.getOldDataParamName(), JSON.toJSONString(change));
			currentProcessTaskStepVo.getParamObj().put("source", source);

			IProcessStepHandlerUtil.audit(currentProcessTaskStepVo, ChangeAuditType.UPDATECHANGE);
		}		
		return null;
	}

}
